//redis缓存实现类
package cn.zf233.jwtshiro.config.shiro;

import cn.zf233.jwtshiro.util.JwtUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Collection;
import java.util.Objects;
import java.util.Set;


public class MyRedisCacheManager implements CacheManager {

    private static final String CACHE_KEY_PREFIX = "shiro:";

    private final StringRedisTemplate redisTemplate;

    public MyRedisCacheManager(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
        return new ShiroRedisCache<>(CACHE_KEY_PREFIX + name);
    }

    /**
     * 为shiro量身定做的一个redis cache,为Authorization cache做了特别优化
     */
    public class ShiroRedisCache<K, V> implements Cache<K, V> {

        private final String cacheKey;

        public ShiroRedisCache(String cacheKey) {
            this.cacheKey = cacheKey;
        }

        @Override
        public V get(K key) throws CacheException {
            BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey);
            Object k = hashKey(key);
            return hash.get(k);
        }

        @Override
        public V put(K key, V value) throws CacheException {
            BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey);
            Object k = hashKey(key);
            hash.put((K) k, value);
            return value;
        }

        @Override
        public V remove(K key) throws CacheException {
            BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey);
            Object k = hashKey(key);
            V value = hash.get(k);
            hash.delete(k);
            return value;
        }

        @Override
        public void clear() throws CacheException {
            redisTemplate.delete(cacheKey);
        }

        @Override
        public int size() {
            BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey);
            return Objects.requireNonNull(hash.size()).intValue();
        }

        @Override
        public Set<K> keys() {
            BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey);
            return hash.keys();
        }

        @Override
        public Collection<V> values() {
            BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey);
            return hash.values();
        }

        protected Object hashKey(K key) {
            // 以下获取当前访问用户的Token中PayLoad所负载的userId作为缓存hash key
            PrincipalCollection pc = (PrincipalCollection) key;
            String token = (String) pc.getPrimaryPrincipal();
            return String.valueOf(JwtUtil.getUserIdByToken(token));
        }
    }
}

// -> hashKey()，个人纪录，不需要查看
// 此处很重要,如果key是登录凭证,那么这是访问用户的授权缓存;将登录凭证转为user对象,
// 返回user的name属性做为hash key,否则会以user对象做为hash key,这样就不好清除指定用户的缓存了